home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung 2 / Power-Programmierung CD 2 (Tewi)(1994).iso / gnu / gnulib / rcs4 / source / co.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-02-07  |  27.5 KB  |  752 lines

  1. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  2.    Distributed under license by the Free Software Foundation, Inc.
  3.  
  4. This file is part of RCS.
  5.  
  6. RCS is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. RCS is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with RCS; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  
  20. Report problems and direct all questions to:
  21.  
  22.     rcs-bugs@cs.purdue.edu
  23.  
  24. */
  25.  
  26. /*
  27.  *                     RCS checkout operation
  28.  */
  29. #ifndef lint
  30. static char rcsid[]=
  31. "$Header: d:/rcs/rcs/co.c 5.5 91/02/07 13:51:26 ROOT_DOS Exp $ Purdue CS";
  32. #endif
  33. /*****************************************************************************
  34.  *                       check out revisions from RCS files
  35.  *****************************************************************************
  36.  */
  37.  
  38.  
  39. /* $Log:    co.c $
  40.  * Revision 5.5  91/02/07  13:51:26  ROOT_DOS
  41.  * Make file read/write before unlinking it
  42.  * 
  43.  * Revision 5.4  90/07/15  20:23:39  lfk
  44.  * checked in with -k by ROOT_DOS at 91.02.07.11.44.42.
  45.  * 
  46.  * Revision 5.4  90/07/15  20:23:39  lfk
  47.  * Most major fixes added between rev 5.1 and rev 5.5:
  48.  *     signals fixed so they work on MS-DOS
  49.  *     Added MKS arguments code so argv can be large
  50.  *     added code to handle slashes a'la Unix
  51.  *     added more file extensions to system from MS-DOS
  52.  * 
  53.  * Revision 5.3  90/07/15  15:11:31  ROOT_DOS
  54.  * 
  55.  * Test check in for ^C problem
  56.  * 
  57.  * Revision 5.2  90/07/15  11:30:48  ROOT_DOS
  58.  * DOS version of RCS 4.0 checked in for MODS
  59.  * by lfk@athena.mit.edu
  60.  * Also update to MSC 6.0
  61.  * 
  62.  * revision 5.1 koya 90/01/25 01:11:44
  63.  * Initial revision
  64.  * 
  65.  * Revision 4.7  89/05/01  15:11:41  narten
  66.  * changed copyright header to reflect current distribution rules
  67.  * 
  68.  * Revision 4.6  88/11/08  12:02:31  narten
  69.  * changes from  eggert@sm.unisys.com (Paul Eggert)
  70.  * 
  71.  * Revision 4.6  88/08/09  19:12:15  eggert
  72.  * Fix "co -d" core dump; rawdate wasn't always initialized.
  73.  * Use execv(), not system(); fix putchar('\0') and diagnose() botches; remove lint
  74.  * 
  75.  * Revision 4.5  87/12/18  11:35:40  narten
  76.  * lint cleanups (from Guy Harris)
  77.  * 
  78.  * Revision 4.4  87/10/18  10:20:53  narten
  79.  * Updating version numbers changes relative to 1.1, are actually
  80.  * relative to 4.2
  81.  * 
  82.  * Revision 1.3  87/09/24  13:58:30  narten
  83.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  84.  * warnings)
  85.  * 
  86.  * Revision 1.2  87/03/27  14:21:38  jenkins
  87.  * Port to suns
  88.  * 
  89.  * Revision 1.1  84/01/23  14:49:58  kcs
  90.  * Initial revision
  91.  * 
  92.  * Revision 4.2  83/12/05  13:39:48  wft
  93.  * made rewriteflag external.
  94.  * 
  95.  * Revision 4.1  83/05/10  16:52:55  wft
  96.  * Added option -u and -f.
  97.  * Added handling of default branch.
  98.  * Replaced getpwuid() with getcaller().
  99.  * Removed calls to stat(); now done by pairfilenames().
  100.  * Changed and renamed rmoldfile() to rmworkfile().
  101.  * Replaced catchints() calls with restoreints(), unlink()--link() with rename();
  102.  * 
  103.  * Revision 3.7  83/02/15  15:27:07  wft
  104.  * Added call to fastcopy() to copy remainder of RCS file.
  105.  *
  106.  * Revision 3.6  83/01/15  14:37:50  wft
  107.  * Added ignoring of interrupts while RCS file is renamed; this avoids
  108.  * deletion of RCS files during the unlink/link window.
  109.  *
  110.  * Revision 3.5  82/12/08  21:40:11  wft
  111.  * changed processing of -d to use DATEFORM; removed actual from
  112.  * call to preparejoin; re-fixed printing of done at the end.
  113.  *
  114.  * Revision 3.4  82/12/04  18:40:00  wft
  115.  * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE.
  116.  * Fixed printing of "done".
  117.  *
  118.  * Revision 3.3  82/11/28  22:23:11  wft
  119.  * Replaced getlogin() with getpwuid(), flcose() with ffclose(),
  120.  * %02d with %.2d, mode generation for working file with WORKMODE.
  121.  * Fixed nil printing. Fixed -j combined with -l and -p, and exit
  122.  * for non-existing revisions in preparejoin().
  123.  *
  124.  * Revision 3.2  82/10/18  20:47:21  wft
  125.  * Mode of working file is now maintained even for co -l, but write permission
  126.  * is removed.
  127.  * The working file inherits its mode from the RCS file, plus write permission
  128.  * for the owner. The write permission is not given if locking is strict and
  129.  * co does not lock.
  130.  * An existing working file without write permission is deleted automatically.
  131.  * Otherwise, co asks (empty answer: abort co).
  132.  * Call to getfullRCSname() added, check for write error added, call
  133.  * for getlogin() fixed.
  134.  *
  135.  * Revision 3.1  82/10/13  16:01:30  wft
  136.  * fixed type of variables receiving from getc() (char -> int).
  137.  * removed unused variables.
  138.  */
  139.  
  140.  
  141.  
  142.  
  143. #include "rcsbase.h"
  144. #include "time.h"
  145. #include <sys/types.h>
  146. #include <sys/stat.h>
  147.  
  148. #ifndef lint
  149. static char rcsbaseid[] = RCSBASE;
  150. #endif
  151. #ifdef MSDOS
  152. static char co[] = CO;
  153. static char merge[] = "\genurcs\src\merge.bat";
  154. #else
  155. static char co[] = CO;
  156. static char merge[] = MERGE;
  157. #endif /* MSDOS */
  158.  
  159. extern FILE * fopen();
  160. extern int    rename();
  161. extern char * getcaller();          /*get login of caller                   */
  162. extern struct hshentry * genrevs(); /*generate delta numbers                */
  163. extern char * getancestor();
  164. extern int  nextc;                  /*next input character                  */
  165. extern int  nerror;                 /*counter for errors                    */
  166. extern char Kdesc[];            /*keyword for description            */
  167. extern char * buildrevision();      /*constructs desired revision           */
  168. extern int    buildjoin();          /*join several revisions                */
  169. extern char * mktempfile();         /*temporary file name generator         */
  170. extern struct hshentry * findlock();/*find (and delete) a lock              */
  171. extern struct lock * addlock();     /*add a new lock                        */
  172. extern long   maketime();           /*convert parsed time to unix time.     */
  173. extern struct tm * localtime();     /*convert unixtime into a tm-structure  */
  174. extern FILE * finptr;               /* RCS input file                       */
  175. extern FILE * frewrite;             /* new RCS file                         */
  176. extern int    rewriteflag;          /* indicates whether input should be    */
  177.                     /* echoed to frewrite                   */
  178.  
  179. char * newRCSfilename, * neworkfilename;
  180. char * RCSfilename, * workfilename;
  181. #ifdef MSDOS
  182. char tmpdir[NCPPN];
  183. char *gettmpdir();
  184. #endif /* MSDOS */
  185. extern struct stat RCSstat, workstat; /* file status of RCS and work file   */
  186. extern int  haveRCSstat, haveworkstat;/* status indicators                  */
  187.  
  188. char * date, * rev, * state, * author, * join;
  189. char finaldate[datelength];
  190.  
  191. int forceflag, lockflag, unlockflag, tostdout;
  192. char * caller;                        /* caller's login;                    */
  193. extern quietflag;
  194.  
  195. char numericrev[revlength];           /* holds expanded revision number     */
  196. struct hshentry * gendeltas[hshsize]; /* stores deltas to be generated      */
  197. struct hshentry * targetdelta;        /* final delta to be generated        */
  198.  
  199. char * joinlist[joinlength];          /* pointers to revisions to be joined */
  200. int lastjoin;                         /* index of last element in joinlist  */
  201.  
  202. #ifdef MKS
  203. main(int argc, char *argv[], char *env[])
  204. #else
  205. main (argc, argv)
  206. int argc;
  207. char * argv[];
  208. #endif /* MKS */
  209. {
  210.         int killock;                  /* indicates whether a lock is removed*/
  211.         char * cmdusage;
  212.         struct tm parseddate, *ftm;
  213.         char * rawdate;
  214.         long unixtime;
  215.  
  216. #ifdef MKS
  217.     int z = 0;
  218.     int ARGC = 0;
  219.     char **tmp;
  220.     char *env_name;
  221. #    define MAXARGS 500        /* This means 500 items on the command line */
  222.     if (!(tmp = (char **)malloc(sizeof(char *)*(MAXARGS+1)))) {
  223.         fprintf(stderr, "%s: can't allocate space for arguments\n",argv[0]);
  224.         exit(1);
  225.     }
  226.     for ( z = 0 ; env[z] != NULL; z++) {    /* loop through environment */
  227.         if (*env[z] == '~') {    /* testing for entries begining with '~' */
  228.             *++env[z];            /* increment pointer to delete '~' */
  229.             tmp[ARGC++] = env[z];    /* add it to our new array */
  230.             if (z >= MAXARGS) {
  231.                 fprintf(stderr, "%s: can't handle any more arguments\n", argv[0]);
  232.                 goto list;
  233.             }
  234.         }
  235.         else if (*env[z] == '_') {    /* testing for entries begining with _ */
  236.             *++env[z] ; *++env[z];    /* move past the '_' and the '=' */
  237.             env_name = env[z];    /* copy the name */
  238.         }
  239.     }
  240. list:
  241.     if ( STREQ( (char *) argv[0] , env_name ) ) {    /* test name against startup args */
  242.         /* environment arguments meant for this program */
  243. #    ifdef DEBUG
  244.         printf("Using shell supplied args\n");
  245. #    endif
  246.         argc = ARGC;
  247.         tmp[ARGC] = NULL;    /* the terminal NULL */
  248.         argv = tmp;
  249.     }
  250. #    ifdef DEBUG
  251.     else 
  252.         printf("Using startup supplied args\n");
  253. #    endif /* debug */
  254. #endif /* MKS */
  255.  
  256.         catchints();
  257.            cmdid = "co";
  258.         cmdusage = "command format:\nco -f[rev] -l[rev] -p[rev] -q[rev] -r[rev] -ddate -sstate -w[login] -jjoinlist file ...";
  259.         date = rev = state = author = join = nil;
  260.         forceflag = lockflag = unlockflag = tostdout = quietflag = false;
  261.         caller=getcaller();
  262.         rawdate = "";
  263.  
  264.         while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
  265.                 switch ((*argv)[1]) {
  266.  
  267.                 case 'r':
  268.                 revno:  if ((*argv)[2]!='\0') {
  269.                                 if (rev!=nil) warn("Redefinition of revision number");
  270.                                 rev = (*argv)+2;
  271.                         }
  272.                         break;
  273.  
  274.         case 'f':
  275.             forceflag=true;
  276.             goto revno;
  277.  
  278.                 case 'l':
  279.                         lockflag=true;
  280.                         if (unlockflag) {
  281.                                 warn("-l has precedence over -u");
  282.                                 unlockflag=false;
  283.                         }
  284.                         goto revno;
  285.  
  286.                 case 'u':
  287.                         unlockflag=true;
  288.                         if (lockflag) {
  289.                                 warn("-l has precedence over -u");
  290.                                 unlockflag=false;
  291.                         }
  292.                         goto revno;
  293.  
  294.                 case 'p':
  295.                         tostdout=true;
  296.                         goto revno;
  297.  
  298.                 case 'q':
  299.                         quietflag=true;
  300.                         goto revno;
  301.  
  302.                 case 'd':
  303.                         if ((*argv)[2]!='\0') {
  304.                                 if (date!=nil) warn("Redefinition of -d option");
  305.                                 rawdate=(*argv)+2;
  306.                         }
  307.                         /* process date/time */
  308.                         if (partime(rawdate,&parseddate)==0)
  309.                                 faterror("Can't parse date/time: %s",rawdate);
  310.                         if ((unixtime=maketime(&parseddate))== 0L)
  311.                                 faterror("Inconsistent date/time: %s",rawdate);
  312.                         ftm=localtime(&unixtime);
  313.                         VOID sprintf(finaldate,DATEFORM,
  314.                         ftm->tm_year,ftm->tm_mon+1,ftm->tm_mday,ftm->tm_hour,ftm->tm_min,ftm->tm_sec);
  315.                         date=finaldate;
  316.                         break;
  317.  
  318.                 case 'j':
  319. #ifdef MSDOS /* Current Version can't treat join */
  320.             faterror("Current Version can't treat join");
  321. #else            
  322.                         if ((*argv)[2]!='\0'){
  323.                                 if (join!=nil)warn("Redefinition of -j option");
  324.                                 join = (*argv)+2;
  325.                         }
  326.                         break;
  327. #endif /* MSDOS */
  328.  
  329.                 case 's':
  330.                         if ((*argv)[2]!='\0'){
  331.                                 if (state!=nil)warn("Redefinition of -s option");
  332.                                 state = (*argv)+2;
  333.                         }
  334.                         break;
  335.  
  336.                 case 'w':
  337.                         if (author!=nil)warn("Redefinition of -w option");
  338.                         if ((*argv)[2]!='\0')
  339.                                 author = (*argv)+2;
  340.                         else    author = caller;
  341.                         break;
  342.  
  343.                 default:
  344.                         faterror("unknown option: %s\n%s", *argv,cmdusage);
  345.  
  346.                 };
  347.         } /* end of option processing */
  348.  
  349.         if (argc<1) faterror("No input file\n%s",cmdusage);
  350.  
  351. #ifdef MSDOS
  352.     strcpy( tmpdir, gettmpdir() );
  353. #endif /* MSDOS */
  354.         /* now handle all filenames */
  355.         do {
  356.         rewriteflag=false;
  357.         finptr=frewrite=NULL;
  358.         neworkfilename=nil;
  359.  
  360.         if (!pairfilenames(argc,argv,true,tostdout)) continue;
  361.  
  362.         /* now RCSfilename contains the name of the RCS file, and finptr
  363.          * the file descriptor. If tostdout is false, workfilename contains
  364.          * the name of the working file, otherwise undefined (not nil!).
  365.          * Also, RCSstat, workstat, and haveworkstat have been set.
  366.          */
  367.         diagnose("%s  -->  %s", RCSfilename,tostdout?"stdout":workfilename);
  368.  
  369.  
  370.         if (!tostdout && !trydiraccess(workfilename)) continue; /* give up */
  371.         if ((lockflag||unlockflag) && !checkaccesslist(caller)) continue;     /* give up */
  372.         if (!trysema(RCSfilename,lockflag||unlockflag)) continue;           /* give up */
  373.  
  374.  
  375.         gettree();  /* reads in the delta tree */
  376.  
  377.         if (Head==nil) {
  378.                 /* no revisions; create empty file */
  379.                 diagnose("no revisions present; generating empty revision 0.0");
  380.                 if (!tostdout)
  381.                         if (!creatempty()) continue;
  382.                 /* Can't reserve a delta, so don't call addlock */
  383.         } else {
  384.                 if (rev!=nil) {
  385.                         /* expand symbolic revision number */
  386.                         if (!expandsym(rev,numericrev))
  387.                                 continue;
  388.         } elsif (unlockflag && (targetdelta=findlock(caller,false))!=nil) {
  389.             VOID strcpy(numericrev,targetdelta->num);
  390.                 } elsif (Dbranch!=nil) {
  391.                         VOID strcpy(numericrev,Dbranch->num);
  392.         } else  numericrev[0]='\0'; /* empty */
  393.                 /* get numbers of deltas to be generated */
  394.                 if (!(targetdelta=genrevs(numericrev,date,author,state,gendeltas)))
  395.                         continue;
  396.                 /* check reservations */
  397.                 if (lockflag && !addlock(targetdelta,caller))
  398.                         continue;
  399.  
  400.                 if (unlockflag) {
  401.                         if((killock=rmlock(caller,targetdelta))== -1)
  402.                                 continue;
  403.                 } else {
  404.                         killock=0;
  405.                 }
  406.  
  407.                 if (join && !preparejoin()) continue;
  408.  
  409.         diagnose("revision %s%s",targetdelta->num,
  410.              lockflag?" (locked)":
  411.              unlockflag?" (unlocked)":"");
  412.  
  413.                 /* remove old working file if necessary */
  414.                 if (!tostdout)
  415.                         if (!rmworkfile()) continue;
  416.  
  417.                 /* prepare for rewriting the RCS file */
  418.                 if (lockflag||(killock==1)) {
  419.                         newRCSfilename=mktempfile(RCSfilename,NEWRCSFILE);
  420.                         if ((frewrite=fopen(newRCSfilename, "w"))==NULL) {
  421.                                 error("Can't open file %s",newRCSfilename);
  422.                                 continue;
  423.                         }
  424.                         putadmin(frewrite);
  425.                         puttree(Head,frewrite);
  426.                         VOID fprintf(frewrite, "\n\n%s%c",Kdesc,nextc);
  427.                         rewriteflag=true;
  428.                 }
  429.  
  430.                 /* skip description */
  431.                 getdesc(false); /* don't echo*/
  432.  
  433.                 if (!(neworkfilename=buildrevision(gendeltas,targetdelta,
  434. #ifdef MSDOS
  435.                       tostdout?(join!=nil? tmpdir:(char *)nil):workfilename,true)))
  436. #else
  437.                       tostdout?(join!=nil?"/tmp/":(char *)nil):workfilename,true)))
  438. #endif /* MSDOS */
  439.                                 continue;
  440.  
  441.                 if ((lockflag||killock==1)&&nerror==0) {
  442.                         /* rewrite the rest of the RCSfile */
  443.                         fastcopy(finptr,frewrite);
  444.                         ffclose(frewrite); frewrite=NULL;
  445.             ignoreints();
  446. #ifdef MSDOS
  447.                         if (Rename(newRCSfilename,RCSfilename) != 0 ) {
  448. #else
  449.                         if (rename(newRCSfilename,RCSfilename)<0) {
  450. #endif /* MSDOS */
  451.                                 error("Can't rewrite %s; saved in: %s",
  452.                                 RCSfilename, newRCSfilename);
  453.                                 newRCSfilename[0]='\0'; /* avoid deletion*/
  454.                                 restoreints();
  455.                                 break;
  456.                         }
  457.                         newRCSfilename[0]='\0'; /* avoid re-deletion by cleanup()*/
  458. #ifdef MSDOS
  459.                         if (chmod(RCSfilename,RCSstat.st_mode & ~S_IWRITE)<0)
  460. #else
  461.                         if (chmod(RCSfilename,RCSstat.st_mode & ~0222)<0)
  462. #endif /* MSDOS */
  463.                             warn("Can't preserve mode of %s",RCSfilename);
  464.                         restoreints();
  465.                 }
  466.  
  467. #               ifdef SNOOPFILE
  468.                 logcommand("co",targetdelta,gendeltas,caller);
  469. #               endif
  470.  
  471.                 if (join) {
  472.                         rmsema(); /* kill semaphore file so other co's can proceed */
  473.             if (!buildjoin(neworkfilename)) continue;
  474.                 }
  475.                 if (!tostdout) {
  476. #ifdef MSDOS
  477.             if (Rename(neworkfilename,workfilename) <0) {
  478. #else
  479.             if (rename(neworkfilename,workfilename) <0) {
  480. #endif /* MSDOS */
  481.                                 error("Can't create %s; see %s",workfilename,neworkfilename);
  482.                                 neworkfilename[0]= '\0'; /*avoid deletion*/
  483.                                 continue;
  484.                         }
  485.             neworkfilename[0]= '\0'; /*avoid re-deletion by cleanup()*/
  486.         }
  487.         }
  488.     if (!tostdout)
  489.             if (chmod(workfilename, WORKMODE(RCSstat.st_mode))<0)
  490.                 warn("Can't adjust mode of %s",workfilename);
  491.  
  492.  
  493.         if (!tostdout) diagnose("done");
  494.         } while (cleanup(),
  495.                  ++argv, --argc >=1);
  496.  
  497.         exit(nerror!=0);
  498.  
  499. }       /* end of main (co) */
  500.  
  501.  
  502. /*****************************************************************
  503.  * The following routines are auxiliary routines
  504.  *****************************************************************/
  505.  
  506. int rmworkfile()
  507. /* Function: unlinks workfilename, if it exists, under the following conditions:
  508.  * If it is read-only, workfilename is unlinked.
  509.  * Otherwise (file writable):
  510.  *   if !quietmode asks the user whether to really delete it (default: fail);
  511.  *   otherwise failure.
  512.  * Returns false on failure to unlink, true otherwise.
  513.  */
  514. {
  515.         int response, c;    /* holds user response to queries */
  516.  
  517.         if (haveworkstat< 0)      /* File doesn't exist; set by pairfilenames*/
  518.             return (true);        /* No problem */
  519.  
  520. #ifdef MSDOS
  521.     if ((workstat.st_mode & S_IWRITE)&&!forceflag) {    /* File is writable */
  522. #else
  523.     if ((workstat.st_mode & 0222)&&!forceflag) {    /* File is writable */
  524. #endif /* MSDOS */
  525.             if (!quietflag) {
  526.                 VOID fprintf(stderr,"writable %s exists; overwrite? [ny](n): ",workfilename);
  527.                 /* must be stderr in case of IO redirect */
  528.                 c=response=getchar();
  529.                 while (!(c==EOF || c=='\n')) c=getchar(); /*skip rest*/
  530.                 if (!(response=='y'||response=='Y')) {
  531.                         warn("checkout aborted.");
  532.                         return false;
  533.                 }
  534.             } else {
  535.                 error("writable %s exists; checkout aborted.",workfilename);
  536.                 return false;
  537.             }
  538.         }
  539. #ifdef MSDOS
  540.     else    /* DOS can't unlink a read-only file */
  541.         chmod( workfilename, S_IWRITE | S_IREAD );
  542. #endif /* MSDOS */
  543.     /* now unlink: either not writable, forceflag, or permission given */
  544.         if (unlink(workfilename) != 0) {            /* Remove failed   */
  545.             error("Can't unlink %s",workfilename);
  546.             return false;
  547.         }
  548.         return true;
  549. }
  550.  
  551.  
  552. creatempty()
  553. /* Function: creates an empty working file.
  554.  * First, removes an existing working file with rmworkfile().
  555.  */
  556. {
  557.         int  fdesc;              /* file descriptor */
  558.  
  559.         if (!rmworkfile()) return false;
  560.         fdesc=creat(workfilename,0);
  561.         if (fdesc < 0) {
  562.                 faterror("Cannot create %s",workfilename);
  563.                 return false;
  564.         } else {
  565.                 VOID close(fdesc); /* empty file */
  566.                 return true;
  567.         }
  568. }
  569.  
  570.  
  571. int rmlock(who,delta)
  572. char * who; struct hshentry * delta;
  573. /* Function: removes the lock held by who on delta.
  574.  * Returns -1 if someone else holds the lock,
  575.  * 0 if there is no lock on delta,
  576.  * and 1 if a lock was found and removed.
  577.  */
  578. {       register struct lock * next, * trail;
  579.         char * num;
  580.         struct lock dummy;
  581.         int whomatch, nummatch;
  582.  
  583.         num=delta->num;
  584.         dummy.nextlock=next=Locks;
  585.         trail = &dummy;
  586.         while (next!=nil) {
  587.                 whomatch=strcmp(who,next->login);
  588.                 nummatch=strcmp(num,next->delta->num);
  589.                 if ((whomatch==0) && (nummatch==0)) break;
  590.                      /*found a lock on delta by who*/
  591.                 if ((whomatch!=0)&&(nummatch==0)) {
  592.                     error("revision %s locked by %s; use co -r or rcs -u",num,next->login);
  593.                     return -1;
  594.                 }
  595.                 trail=next;
  596.                 next=next->nextlock;
  597.         }
  598.         if (next!=nil) {
  599.                 /*found one; delete it */
  600.                 trail->nextlock=next->nextlock;
  601.                 Locks=dummy.nextlock;
  602.                 next->delta->lockedby=nil; /* reset locked-by */
  603.                 return 1; /*success*/
  604.         } else  return 0; /*no lock on delta*/
  605. }
  606.  
  607.  
  608.  
  609.  
  610. /*****************************************************************
  611.  * The rest of the routines are for handling joins
  612.  *****************************************************************/
  613.  
  614. char * getrev(sp, tp, buffsize)
  615. register char * sp, *tp; int buffsize;
  616. /* Function: copies a symbolic revision number from sp to tp,
  617.  * appends a '\0', and returns a pointer to the character following
  618.  * the revision number; returns nil if the revision number is more than
  619.  * buffsize characters long.
  620.  * The revision number is terminated by space, tab, comma, colon,
  621.  * semicolon, newline, or '\0'.
  622.  * used for parsing the -j option.
  623.  */
  624. {
  625.         register char c;
  626.         register int length;
  627.  
  628.         length = 0;
  629.         while (((c= *sp)!=' ')&&(c!='\t')&&(c!='\n')&&(c!=':')&&(c!=',')
  630.                 &&(c!=';')&&(c!='\0')) {
  631.                 if (length>=buffsize) return false;
  632.                 *tp++= *sp++;
  633.                 length++;
  634.         }
  635.         *tp= '\0';
  636.         return sp;
  637. }
  638.  
  639.  
  640.  
  641. int preparejoin()
  642. /* Function: Parses a join list pointed to by join and places pointers to the
  643.  * revision numbers into joinlist.
  644.  */
  645. {
  646.         struct hshentry * * joindeltas;
  647.         struct hshentry * tmpdelta;
  648.         register char * j;
  649.         char symbolrev[revlength],numrev[revlength];
  650.  
  651.         joindeltas = (struct hshentry * *)talloc(hshsize*sizeof(struct hshentry *));
  652.         j=join;
  653.         lastjoin= -1;
  654.         for (;;) {
  655.                 while ((*j==' ')||(*j=='\t')||(*j==',')) j++;
  656.                 if (*j=='\0') break;
  657.                 if (lastjoin>=joinlength-2) {
  658.                         error("too many joins");
  659.                         return(false);
  660.                 }
  661.                 if(!(j=getrev(j,symbolrev,revlength))) return false;
  662.                 if (!expandsym(symbolrev,numrev)) return false;
  663.                 tmpdelta=genrevs(numrev,(char *)nil,(char *)nil,(char *)nil,(struct hshentry * *)joindeltas);
  664.                 if (tmpdelta==nil)
  665.                         return false;
  666.                 else    joinlist[++lastjoin]=tmpdelta->num;
  667.                 while ((*j==' ') || (*j=='\t')) j++;
  668.                 if (*j == ':') {
  669.                         j++;
  670.                         while((*j==' ') || (*j=='\t')) j++;
  671.                         if (*j!='\0') {
  672.                                 if(!(j=getrev(j,symbolrev,revlength))) return false;
  673.                                 if (!expandsym(symbolrev,numrev)) return false;
  674.                                 tmpdelta=genrevs(numrev,(char *)nil,(char *)nil,(char *)nil, (struct hshentry * *) joindeltas);
  675.                                 if (tmpdelta==nil)
  676.                                         return false;
  677.                                 else    joinlist[++lastjoin]=tmpdelta->num;
  678.                         } else {
  679.                                 error("join pair incomplete");
  680.                                 return false;
  681.                         }
  682.                 } else {
  683.                         if (lastjoin==0) { /* first pair */
  684.                                 /* common ancestor missing */
  685.                                 joinlist[1]=joinlist[0];
  686.                                 lastjoin=1;
  687.                                 /*derive common ancestor*/
  688.                                 joinlist[0]=talloc(revlength);
  689.                                 if (!getancestor(targetdelta->num,joinlist[1],joinlist[0]))
  690.                                        return false;
  691.                         } else {
  692.                                 error("join pair incomplete");
  693.                                 return false;
  694.                         }
  695.                 }
  696.         }
  697.         if (lastjoin<1) {
  698.                 error("empty join");
  699.                 return false;
  700.         } else  return true;
  701. }
  702.  
  703.  
  704.  
  705. buildjoin(initialfile)
  706. char * initialfile;
  707. /* Function: merge pairs of elements in joinlist into initialfile
  708.  * If tostdout==true, copy result to stdout.
  709.  * All unlinking of initialfile, rev2, and rev3 should be done by cleanup().
  710.  */
  711. {
  712.     char commarg[revlength+3];
  713.         char subs[revlength];
  714.         char * rev2, * rev3;
  715.         int i;
  716.  
  717. #ifdef MSDOS
  718.         rev2=mktempfile(tmpdir,JOINFIL2);
  719.         rev3=mktempfile(tmpdir,JOINFIL3);
  720. #else
  721.         rev2=mktempfile("/tmp/",JOINFIL2);
  722.         rev3=mktempfile("/tmp/",JOINFIL3);
  723. #endif /* MSDOS */
  724.  
  725.         i=0;
  726.         while (i<lastjoin) {
  727.                 /*prepare marker for merge*/
  728.                 if (i==0)
  729.                         VOID strcpy(subs,targetdelta->num);
  730.                 else    VOID sprintf(subs, "merge%d",i/2);
  731.                 diagnose("revision %s",joinlist[i]);
  732.                 VOID sprintf(commarg,"-p%s",joinlist[i]);
  733.                 if (run((char*)nil,rev2, co,commarg,"-q",RCSfilename,(char*)nil)) {
  734.                         nerror++;return false;
  735.                 }
  736.                 diagnose("revision %s",joinlist[i+1]);
  737.                 VOID sprintf(commarg,"-p%s",joinlist[i+1]);
  738.                 if (run((char *)nil,rev3, co,commarg,"-q",RCSfilename,(char*)nil)) {
  739.                         nerror++; return false;
  740.                 }
  741.                 diagnose("merging...");
  742.         if (
  743.                         (i+2)>=lastjoin && tostdout
  744.             ?    run((char*)nil,(char*)nil, merge,"-p",initialfile,rev2,rev3,subs,joinlist[i+1],(char*)nil)
  745.             :    run((char*)nil,(char*)nil, merge,     initialfile,rev2,rev3,subs,joinlist[i+1],(char*)nil)) {
  746.                         nerror++; return false;
  747.                 }
  748.                 i=i+2;
  749.         }
  750.         return true;
  751. }
  752.